home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Technotools
/
Technotools (Chestnut CD-ROM)(1993).ISO
/
batch
/
bat
/
batstk.asm
< prev
next >
Wrap
Assembly Source File
|
1985-10-16
|
17KB
|
358 lines
PAGE 60,132
TITLE BATSTACK -- Special device driver for EBL keyboard stack
COMMENT $
BATSTACK Nathaniel R. Goodspeed
--------
Overview
--------
This device driver supports the "keyboard stack" available through use of the
Extended Batch Language, EBL. Normally, the only way to get stuff into the
keyboard stack is to use the EBL commands STACK or BEGSTACK ... END. (Programs
remove entries from the stack by attempting normal keyboard reads, and EBL
provides a way to read the stack into EBL variables.) But BATSTACK allows the
output of any program to be produced into the stack, using normal DOS output
redirection:
echo This is going into the keyboard stack>batstack
Obviously, the use of STACK or BEGSTACK is better than ECHOing into BATSTACK.
But the power of BATSTACK is that any filter can produce output into the
keyboard stack, for use by any other program, or for further processing by
EBL programs.
------------
Installation
------------
BATSTACK is created by assembling BATSTK.ASM, linking it, and converting it
to a .COM-form file with the extension .DEV (so nobody tries to execute it
by typing its name, which would result in chaos).
masm batstk,batstk,batstk,nul;
link batstk;
exe2bin batstk.exe batstk.dev
del batstk.obj
del batstk.exe
Once you have BATSTK.DEV, it is installed by adding a line to your CONFIG.SYS
file of the form:
DEVICE=BATSTK.DEV
You must either ensure that BATSTK.DEV is in the root directory of your boot
disk, or supply the full drive and pathname of its location, e.g.:
DEVICE=C:\DEVICES\BATSTK.DEV
BATSTACK will be installed when the system is next rebooted. It will announce
its presence with the message "BATSTACK driver loaded."
You may be wondering why, since the device is called BATSTACK, the files used
to implement it are called BATSTK.* . The reason is that once a device with
a particular name is installed in your system, DOS ignores any extension given
with that device name! In other words, if the BATSTACK source and device files
were named BATSTACK.ASM and BATSTACK.DEV, DOS would forget that the disk files
were there as soon as the BATSTACK device was installed. The command line
"type batstack.asm" would be treated the same as "type batstack", and would
cause the contents of the stack to be typed! (This is why it's not possible
to create a disk file called CON.TXT, too.)
-----------
Usage Notes
-----------
There are two cases in which you might want to redirect a filter's output to
BATSTACK rather than a file or pipe. The first to let an EBL batch file
read the output, so that (for instance) a batch file can know a file's size
without having to display DIR output on the screen and find it using READSCRN.
The second case is when you want the filter to supply some, but not all, of
the input for a program to follow. When a program thinks it's reading the
keyboard, it will read the stack until it is exhausted, then revert to reading
the real keyboard. So to supply the first few commands for program2 from
program1, you could place in a batch file the lines:
program1 >batstack
program2
By contrast, if you connect the two programs with a DOS pipeline:
program1 | program2
or redirect the output of the first into a file, and read that with the second:
program1 >miscfile
program2 <miscfile
then program1 had better generate an explicit command to cause program2 to
terminate, or else program2 might reach end-of-file on the pipeline or file and
wait indefinitely for a termination command to appear, without allowing it to
be typed from the real keyboard! If program2 is itself a filter, or at any
rate was coded to recognize end-of-file on standard input, it will terminate at
end-of-file. But in no case will it automatically switch to the real keyboard.
Don't try to copy a large amount of text into BATSTACK, however. The EBL 2.04a
stack handler apparently isn't very careful about checking for stack-full, at
least not when text is entered with the INT 16H, AH=73H option. Not only is
there no defined way to detect a stack-full condition on return from the INT,
but when I attempted to copy a 537-byte file into a stack initialized with the
usual BAT * 512 command, the ctrl-PrtSc key stopped working! (DOS echoed ^P
at the command prompt rather than echoing following characters to the printer.)
--------------------
Implementation Notes
--------------------
The Extended Batch Language STACK and BEGSTACK commands normally delimit each
line in the stack with a CR, rather than a CR/LF as DOS text lines normally end.
This is consistent with the pretext that the stack contains user keystrokes;
the user never types an explicit LF. However, since the BATSTACK driver is
expected to handle normal DOS text files, it performs a transformation: when
CR/LF is written to BATSTACK, the LF is discarded; when CR is read from BATSTACK,
an LF is automatically appended. In theory, unless the text written to BATSTACK
already contains CR characters without LFs, this should be transparent if the
BATSTACK driver is both written and read by DOS:
copy myfile batstack
copy batstack myfile.new
But when the stack is read via keyboard requests, no spurious LFs will show up.
Another funny convention followed by BATSTACK when being read as a DOS device
is that there is always a character ready to be read -- but if the stack is
empty or disabled, the character returned by BATSTACK will be a simulated
ctrl-Z, to cause DOS programs to recognize end-of-file. The DOS device driver
documentation suggests that if a driver sets the "busy" bit meaning that no
character is currently available, DOS will wait under some circumstances until
the "busy" bit is reset. We can be almost positive, however, that if the stack
is empty or disabled, no new characters will appear in it no matter how long
DOS might wait. This is why we always claim that we have a character ready.
A useful enhancement to the EBL INT 16H stack support would be a status return
on functions 73H and 74H, so programs could tell if the write failed. Another
would be a function to request the current stack enable/disable status and the
length of the current contents. This would let programs specifically interes-
ted in the stack, not the keyboard, read it even if it were disabled, saving
and restoring the user's enable/disable state.
NRG 09/05/85
$
PAGE
MYSTACKSIZE EQU 256 ;DOS MANUAL CLAIMS DEVICE DRIVERS NEED OWN STACK
LF EQU 0AH ;LINE-FEED CHARACTER
CR EQU 0DH ;CARRIAGE-RETURN CHARACTER
EOF EQU 1AH ;CTRL-Z IS END-OF-FILE MARKER
LJZ MACRO DEST
LOCAL NZDEST
JNZ NZDEST
JMP DEST
NZDEST:
ENDM
DEVATTR RECORD DEVCHAR:1=0, DEVIOCTL:1=0, DEVNONIBM:1=0, UNUSED:9=0, DEVCLOCK:1=0, DEVNUL:1=0, DEVSTDOUT:1=0, DEVSTDIN:1=0
DUMMY STRUC
WORDSIZE DW ?
DUMMY ENDS
; DEVICE HEADER
CSEG SEGMENT BYTE PUBLIC 'CODE'
DD -1 ;REQUIRED; WILL BECOME PTR TO NEXT DRIVER
DEVATTR <1,0> ;DEVICE ATTRIBUTES: CHAR, NO IOCTL SUPPORT
DW OFFSET STRATEGY ;PTR TO DEVICE STRATEGY ROUTINE
DW OFFSET INTERRUPT ;PTR TO DEVICE INTERRUPT ROUTINE
DB 'BATSTACK' ;DEVICE NAME -- BETTER BE DIFFERENT FROM
;OUR SOURCE, BINARY ETC. OR WE CAN'T ACCESS
;DISK FILES ONCE IT'S INSTALLED!
REQHDR DD ? ;SAVE REQUEST HEADER PTR HERE
MYSP DW -2 ;FIRST CALL, SET SP ALMOST AS HIGH AS CAN REACH
;(FOR INIT, WE HAVE ALL OF MEMORY TO OURSELVES)
OLDSP DW ? ;SAVE DOS SP/SS HERE
OLDSS DW ?
LASTREAD DB 00H ;LAST CHARACTER READ FROM STACK
LASTWRITE DB 00H ;LAST CHARACTER WRITTEN TO STACK
STRATEGY PROC FAR ;ENTER HERE FOR STRATEGY ROUTINE
ASSUME CS:CSEG ;ONLY CS POINTS TO US
MOV WORD PTR REQHDR,BX ;SAVE PTR TO REQUEST HEADER
MOV WORD PTR REQHDR+2,ES
RET ;SAVED REQUEST HEADER PTR; RETURN
STRATEGY ENDP
PAGE
FUNCTAB EQU THIS WORD ;TABLE OF FUNCTIONS, SUBSCRIPTED BY COMMAND CODE
DW INIT ; 0 -- INIT
DW DONE ; 1 -- MEDIA CHECK (NOP FOR CHARACTER DEV)
DW DONE ; 2 -- BUILD BPB (NOP FOR CHARACTER DEV)
DW UNKNOWN ; 3 -- IOCTL INPUT
DW READ ; 4 -- INPUT (READ)
DW TESTREAD ; 5 -- NON-DESTRUCTIVE INPUT NO WAIT
DW INSTATUS ; 6 -- INPUT STATUS
DW INFLUSH ; 7 -- INPUT FLUSH
DW WRITE ; 8 -- OUTPUT (WRITE)
DW WRITE ; 9 -- OUTPUT (WRITE) WITH VERIFY
DW DONE ;10 -- OUTPUT STATUS
DW DONE ;11 -- OUTPUT FLUSH (WE DON'T BUFFER OUTPUT)
DW UNKNOWN ;12 -- IOCTL OUTPUT
FUNCCNT EQU ($-FUNCTAB)/(TYPE WORDSIZE)
INTERRUPT PROC FAR ;HERE TO PERFORM THE REQUEST JUST 'ENQUEUED'
ASSUME CS:CSEG
PUSH DS
PUSH SI
PUSH ES
PUSH DI
PUSH AX
PUSH BX
PUSH CX
MOV AX,CS ;PTR TO THIS SEGMENT
MOV OLDSP,SP ;SAVE OLD STACK
MOV OLDSS,SS
MOV SS,AX ;SET NEW STACK
MOV SP,MYSP
LDS SI,REQHDR ;GET FULL PTR TO REQUEST HEADER
MOV BL,DS:[SI+2] ;GET COMMAND CODE
CMP BL,FUNCCNT ;CHECK VALIDITY
JAE UNKNOWN ;NOT RECOGNIZED
XOR BH,BH ;BX = BL
SHL BX,1 ;DOUBLE FOR WORD OFFSET INTO FUNCTAB
CLD ;SET DIRECTION TO 'FORWARD'
JMP FUNCTAB[BX] ;SWITCH TO APPROPRIATE ROUTINE
UNKNOWN:
OR WORD PTR DS:[SI+3],8103H ;SET ERROR, DONE AND 'UNKNOWN COMMAND'
JMP SHORT EXIT
DONE:
OR BYTE PTR DS:[SI+4],01 ;SET DONE BIT
EXIT:
MOV SS,OLDSS ;RESTORE ORIGINAL STACK
MOV SP,OLDSP
POP CX
POP BX
POP AX
POP DI
POP ES
POP SI
POP DS
RET
INTERRUPT ENDP
PAGE
READ PROC NEAR ; 4 -- INPUT (READ)
MOV CX,DS:[SI+18] ;PICK UP BYTE COUNT
JCXZ DONE ;IGNORE TRIVIAL REQUESTS
LES DI,DS:[SI+14] ;PICK UP TRANSFER ADDRESS
READLOOP:
CMP LASTREAD,CR ;WAS THE LAST CHAR WE READ A CR?
JNE REALREAD ;IF NOT, READ ANOTHER CHAR
MOV AL,LF ;LAST CHAR WAS CR: SIMULATE FOLLOWING LF
JMP SHORT READSTORE
REALREAD: ;HERE WHEN NOT SIMULATING LF
MOV AX,7600H ;AH = 76H (CHAR SOURCE TO AL); AL = 00H
INT 16H ;IF BAT NOT INSTALLED, AL NOT CHANGED
;IF BAT INSTALLED, AL = 'K'EYBOARD/'S'TACK
TEST AL,AL ;CHECK FOR UNCHANGED AL
JZ UNKNOWN ;IF BAT NOT INSTALLED, WE DON'T SUPPORT FUNCTION
CMP AL,'S' ;DO WHILE STILL CHARS IN STACK
JNE READDONE
XOR AH,AH ;AH = 0: READ NEXT CHAR
INT 16H ;GET CHAR INTO AL
READSTORE: ;HERE AL HAS CHAR TO RETURN
STOSB ;STORE INTO BUFFER, INCREMENT POINTER
MOV LASTREAD,AL ;SAVE TO CHECK FOR CR/LF SIMULATION
LOOP READLOOP ;LOOP UNTIL ALL REQUESTED BYTES READ
JMP SHORT READX ;AND WHEN THAT'S DONE, SKIP TO EXIT
READDONE: ;HERE WHEN WE CAN'T SATISFY READ REQUEST
MOV AL,EOF ;GET EOF MARKER
STOSB ;SHOW IT TO CALLER
MOV LASTREAD,AL ;KEEP TRACK OF CHARS RETURNED TO CALLER
DEC CX ;WE SATISFIED ONE MORE REQUESTED CHARACTER
READX:
SUB DS:[SI+18],CX ;SUBTRACT CHARS-NOT-READ FROM CHARS-REQUESTED
;(THIS RETURNS CHARS-ACTUALLY-READ)
JMP DONE
READ ENDP
TESTREAD PROC NEAR ; 5 -- NON-DESTRUCTIVE INPUT NO WAIT
MOV AX,7600H ;AH = 76H (CHAR SOURCE TO AL); AL = 00H
INT 16H ;IF BAT NOT INSTALLED, AL NOT CHANGED
;IF BAT INSTALLED, AL = 'K'EYBOARD/'S'TACK
TEST AL,AL ;CHECK FOR UNCHANGED AL
JZ UNKNOWN ;IF BAT NOT INSTALLED, WE DON'T SUPPORT FUNCTION
CMP AL,'S' ;IF NO CHARS IN STACK
JE TEST_READ_STACK
MOV AL,EOF ;RETURN EOF CHAR IN THAT CASE
JMP SHORT TESTRET
TEST_READ_STACK: ;STACK CHAR IS AVAILABLE
XOR AH,AH ;AH = 0: READ NEXT CHAR FROM STACK
INT 16H ;GET CHAR INTO AL
MOV AH,74H ;PUT THAT CHAR BACK ON TOP OF STACK
INT 16H
TESTRET:
MOV DS:[SI+13],AL ;SHOW CHAR TO CALLER
JMP DONE
TESTREAD ENDP
PAGE
INSTATUS PROC NEAR ; 6 -- INPUT STATUS
JMP DONE ;NEVER SET 'BUSY' BIT
INSTATUS ENDP
INFLUSH PROC NEAR ; 7 -- INPUT FLUSH
MOV AH,77H ;PURGE THE STACK
INT 16H
JMP DONE
INFLUSH ENDP
WRITE PROC NEAR ;8, 9 -- OUTPUT (WRITE) [WITH VERIFY]
MOV CX,DS:[SI+18] ;PICK UP BYTE COUNT
JCXZ DONE ;IGNORE TRIVIAL REQUESTS
MOV AX,7600H ;REQUEST 'K' OR 'S' IN AL, CLEAR AL
INT 16H ;AL UNCHANGED IF EBL NOT INSTALLED
TEST AL,AL ;CHECK FOR UNCHANGED AL
LJZ UNKNOWN ;USELESS TO WRITE TO BATSTACK WITHOUT EBL
PUSH DS
PUSH SI
LDS SI,DS:[SI+14] ;PICK UP TRANSFER ADDRESS
WRITELOOP:
LODSB ;GET NEXT CHAR INTO AL
CMP LASTWRITE,CR ;WAS PREVIOUS CHARACTER CR?
MOV LASTWRITE,AL ;IN ANY CASE, UPDATE LAST-WRITTEN CHAR
JNE WRITEOK ;NOT CR, IN WHICH CASE ALLOW EXPLICIT LF
CMP AL,LF ;LINE FEED: IGNORE THEM (CR/LF --> JUST CR)
JE NOWRITE
WRITEOK:
MOV AH,73H ;STACK AL'S CHAR FIFO STYLE
INT 16H ;"WRITE" THE CHAR
;; WE'D LIKE TO CHECK FOR SUCCESSFUL WRITE, BUT NO STATUS IS DOCUMENTED!
NOWRITE:
LOOP WRITELOOP ;LOOP UNTIL ALL REQUESTED BYTES WRITTEN
POP SI
POP DS
SUB DS:[SI+18],CX ;SUBTRACT CHARS-NOT-WRITTEN FROM CHARS-REQUESTED
;(THIS RETURNS CHARS-ACTUALLY-WRITTEN)
JMP DONE
WRITE ENDP
TOP EQU THIS BYTE ;TRUNCATE HERE AFTER INITIALIZATION
PAGE
STARTMSG DB 0DH,0AH,'BATSTACK driver loaded',0DH,0AH,'$'
INIT PROC NEAR ; 0 -- INIT
PUSH DI
PUSH ES
PUSH AX
PUSH BX
PUSH CX
PUSH DS ;SAVE AGAIN; WE CHANGE FOR DOS CALL
PUSH SI
PUSH DX
PUSH BP
MOV AX,CS ;PTR TO CSEG
MOV DS,AX ;GET DS->CSEG
MOV AH,9 ;DOS PRINT STRING
MOV DX,OFFSET STARTMSG ;DS:DX -> MESSAGE
INT 21H ;ASK DOS TO OUTPUT THE STRING
POP BP
POP DX
POP SI
POP DS ;RESTORE REQHDR SEG PTR
POP CX
POP BX
POP AX
POP ES
POP DI
MOV WORD PTR DS:[SI+16],CS ;SEGMENT PART OF ENDING ADDRESS
MOV WORD PTR DS:[SI+14],OFFSET TOP+MYSTACKSIZE ;OFFSET PART OF ENDING ADDR
MOV MYSP,OFFSET TOP+MYSTACKSIZE ;NEXT ENTRY, USE HIGH END AS STACK
JMP DONE
INIT ENDP
CSEG ENDS
END